#include <iostream>
#include <vector>
#include <cassert>
#include <unistd.h>

using namespace std;

// 这里的类当做结构体使用(方便理解)
class ThreadData
{
public:
    // 编号
    int number;
    // tid
    pthread_t tid;
    // 将数据刷新到buffer中
    char namebuffer[64];
};

// 将返回信息定义成对象
class ThreadReturn
{
public:
    int exit_code;
    int exit_result;
};

// 函数结构必须和pthread_create提供的接口相同
void *start_routine(void *args)
{
    sleep(3);
    ThreadData *td = static_cast<ThreadData *>(args);
    int cnt = 10;
    while (cnt)
    {
        cout << "cnt: " << cnt << " &cnt: " << &cnt << endl;
        cnt--;
        sleep(1);
    }

    // 线程如何终止
    // delete td;
    // 线程函数结束时，return的时候线程就算终止了
    // return nullptr;
    // 传编号过去
    // return (void *)6; // 这里会出现waring -- 将整数强转成指针类型 指针类型在64位机器上是8字节的
    // return (void*)td->number;

    // pthread_exit((void*)106); --既然假的地址，整数都能被外部拿到，那么如何返回的是，堆空间的地址呢？对象的地址呢？
    // 定义成对象
    ThreadReturn *tr = new ThreadReturn();
    // 自定义
    tr->exit_code = 1;
    tr->exit_result = 106;

    // 不能定义成ThreadReturn tr; --这样就是在栈上开辟空间了
    return (void*)tr;
}

int main()
{
    // 1. 想要创建一批线程
    // 放在vector容器中
    vector<ThreadData *> threads;
#define NUM 10
    for (int i = 0; i < NUM; i++)
    {
        // new出ThreadData对象
        ThreadData *td = new ThreadData();
        td->number = i + 1;
        // 打印到namebuffer中
        snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i + 1);
        // 创建线程
        // argv参数先不学习 设置为nullptr 创建start_routine回调函数 也就是线程执行任务
        pthread_create(&td->tid, nullptr, start_routine, td);
        threads.push_back(td);
        // sleep(10);
    }

    for (auto &iter : threads)
    {
        cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " success" << endl;
        // sleep(1);
    }

    // 线程也可以被取消！调用pthread_cancel方法
    // 但是线程要被取消的前提是该线程已经跑起来了！
    // sleep(5);
    // 取消一半的线程
    // for (int i = 0; i < threads.size() / 2; i++)
    // {
    //     // int pthread_cancel(pthread_t thread);
    //     // 线程如果是被取消的 其退出码是-1
    //     pthread_cancel(threads[i]->tid);
    //     cout << "pthread_cancel : " << threads[i]->namebuffer << " success" << endl;
    // }

    // 线程也是需要等待的，如果不进行等待就会造成类似僵尸进程的问题 -- 内存泄漏
    // 线程等待的作用：
    // 1. 获取新线程的退出信息 -- 也可以不关心
    // 2. 回收新线程的PCB等内核资源，防止内存泄漏 -- 暂时无法查看
    for (auto &iter : threads)
    {
        void *ret = nullptr;
        // pthread_join函数默认调用成功！在线程中不考虑异常问题(异常是进程考虑的)
        int n = pthread_join(iter->tid, (void **)&ret);
        // int n = pthread_join(iter->tid, &ret);
        // 若是等待不成功那就直接报错
        assert(n == 0);
        cout << " join : " << iter->namebuffer << " success ,exit_code: " << ((ThreadReturn*)ret)->exit_code << " exit_result: " << ((ThreadReturn*)ret)->exit_result << endl;
        delete iter;
    }
    cout << "main thread quit " << endl;

    return 0;
}